Categories
JavaScript Mistakes

JavaScript Mistakes — null vs undefined, Constructors, and Scopes

Spread the love

JavaScript is a language that’s friendlier than many other programming languages in the world. However, it’s still very easy to make mistakes when writing JavaScript code through misunderstanding or overlooking stuff that we already know. By avoiding some of the mistakes below, we can make our lives easier by preventing bugs and typos in our code that bog us down with unexpected results. In this article, we’ll look at the confusion between null and undefined, scope issues with asynchronous code, and scopes and using object literals vs. constructors for built-in objects.

Confusing Null and Undefined

In JavaScript, both null and undefined mean no value for a variable. They’re pretty similar but there are some important differences. One if that null is of type object, so when we use the typeof operator on null as we do below:

typeof null

We get 'object'.

On the other hand, if we use the typeof operator on undefined, as we do below:

typeof undefined

We get 'undefined'.

If we compare them with the === operator, as we do below:

undefined === null

We get false since they’re of 2 different types. However, when we compare them with the == operator:

undefined == null

Then we get true since they’re both falsy. Since they’re 2 different types, they shouldn’t be confused with each other.

One thing to note is that for function default parameters, they will only be assigned if the value is undefined and not null.

const hello = (name = 'World') => console.log(`Hello, ${name}!`)
hello(null) // Hello, null!
hello(undefined) // Hello, World!

Asynchronous Code and Scope

One confusing aspect of JavaScript is that the visibility of a variable remains in the parent scope. This means that when a loop runs with a loop variable that we want to access in the callback of the setTimeout function, the index variable would have already been incremented to meet the end condition before the callback in setTimeout is called. This means that we’ll only get the final value of the loop variable after the loop is finished, which gets passed into the callback of the setTimeout function.

For example, if we have the following code:

for (var i = 0; i < 10; i++) {
  setTimeout(() => {
    console.log(i);
  }, 100);
}

We get 10 logged 10 times. This is because the code in the setTimeout ‘s callback function runs after the loop is finished. i would have been updated to 10 by then. If we want to keep the value of the loop variable in each setTimeout callback function call. Then we have to pass the value of i into the callback function by wrapping the setTimeout inside a function.

If we want to see 0 to 9 logged instead, then we’ve to write the following:

for (var i = 0; i < 10; i++) {
  ((i) => {
    setTimeout(() => {
      console.log(i);
    }, 100);
  })(i)
}

The code above wraps the setTimeout call inside the anonymous function, which we call every time the loop runs, and we pass in the value of i in each iteration into the function. Then the value of i will be passed into the console.log rather than the value of i after the loop is done.

Photo by Francesco De Tommaso on Unsplash

Using Constructors over Literals

In JavaScript, constructors aren’t always useful for creating new instances of objects. This is especially true for built-in objects like arrays, numbers, and strings. We construct new instances of objects with the new operator like other languages such as C# or Java. However, it doesn’t always do what we expect.

For example, we can create a new Array object with the new operator. For example, we can write:

let arr = new Array('a', 'b', 'c', 'd');

Then we get [“a”, “b”, “c”, “d”] like we expect. However, when we only pass in one argument to the Array constructor, as we do in the code below:

let arr1 = new Array(23);

Then we get an empty array with length property set to 23.

It turns out that there’re 2 Array constructors. One takes one argument, which is the length of the array, and the other takes an infinite list of objects separated by commas, which are the list of elements for the new array.

However, if the one and only argument isn’t a number, then it does create an array with the argument as the only entry of the array. For example, if we write:

let arr = new Array('a');

We get [“a”] .

If the only argument is a negative number, it also fails to create a new array. We get the error message Uncaught RangeError: Invalid array length .

Using the Array constructor is confusing because there’re 2 constructors that take different kinds of arguments.

For numbers and strings, we have different problems when we use constructors to create these objects. When we use the new operator to create a number or string object, we get problems when we compare them with the === operator. For example, if we try to compare 2 strings with the same content where one is a string literal and the other is created with the constructor, as we have in the following code:

'abc' === new String('abc');

We get false even though their content are exactly the same. This is because the string literal 'abc' is of type string, but the object created with new String(‘abc’) is of type object. Again, this is confusing.

We have to call the valueOf method on the string object to make it a string type object.

Likewise, if we have:

1 === new Number(1);

We also get false for the same reason. 1 is of type number while, new Number(1) is of type object. Again, we have to call the valueOf method to convert new Number(1) back to a number-type object.

The solution to all these problems is solved by using the literal notation whenever it’s available. JavaScript doesn’t need to know the length of the array beforehand since it just assigns undefined to whatever position that doesn’t have a value assigned to it.

Conclusion

In JavaScript, both null and undefined means no value for a variable that’s been assigned these values. They’re pretty similar but there’re some important. One if that null is of type object , and undefined is of type undefined . This means that null === undefined will be evaluated to false .

Whenever we have asynchronous code and we want to pass in some variable’s value into a callback that’s called by an asynchronous function like setTimeout , we have to pass in the outside variable into the callback function wrapping the asynchronous code with a function and then pass in the variable from the outside to the callback.

Finally, we should avoid using constructors for creating built-in objects like arrays, numbers, and strings. For numbers and strings, the type will be different from the literal type which will have the actual primitive types of number or string. For arrays, it’s because there’re 2 constructors which take different arguments and we’ll get unexpected results depending on the numbers of arguments we pass in.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *